﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using Seven.Tools;
using Seven.Tools.Extension;
using Seven.Tools.Collections.Concurrent;

namespace Seven.Member.Internal
{
    internal class MemberTokenCollection : ObservableConcurrentDictionary<Guid, MemberToken>
    {
        private Timer _timer;
        private object _locker = new object();

        #region 构造函数定义

        /// <summary>
        /// 初始化类型 <see cref="MemberTokenCollection"/> 的新实例。
        /// </summary>
        internal MemberTokenCollection()
        {
            this.InitTimer();
        }

        #endregion

        #region 外部方法定义

        internal void Add(MemberToken token)
        {
            Check.NotNull(token);
            this.Add(token.Guid, token);
        }

        internal new void Add(Guid key, MemberToken token)
        {
            Check.NotNull(token);
            Check.Equals(key, token.Guid);
            base.Add(key, token);
        }


        internal void RemoveByAccount(string account)
        {
            string temp = account != null ? account.Trim() : string.Empty;
            Check.NotEmpty(temp);

            MemberToken[] tokens = this.GetTokenByAccount(temp);
            IEnumerable<Guid> keys = tokens.Select(s => s.Guid);
            this.TryRemoveRange(keys);
        }

        internal void RemoveByGuid(string key)
        {
            string temp = key != null ? key.Trim() : string.Empty;
            Check.NotEmpty(temp);

            Guid guid = Guid.Empty;
            if (Guid.TryParse(temp, out guid))
            {
                this.RemoveByGuid(guid);
            }
        }

        internal void RemoveByGuid(Guid key)
        {
            MemberToken token = this.GetTokenByGuid(key);
            this.Remove(token);
        }

        internal void Remove(MemberToken token)
        {
            if (token == null)
            { return; }

            this.Remove(token.Guid);
        }

        internal void RemoveByIMEI(string imei)
        {
            string temp = imei != null ? imei.Trim() : string.Empty;
            Check.NotEmpty(temp);
            MemberToken token = this.GetTokenByIMEI(temp);
            this.Remove(token);
        }


        /// <summary>
        /// 根据指定的登录帐号（用户名或登录别名）获取该用户的在所有设备上的登录身份信息。
        /// <para>如果该方法返回一个空数组，则说明该用户无效或者尚未登录。</para>
        /// </summary>
        /// <param name="account">登录帐号（用户名或登录别名）</param>
        /// <returns></returns>
        internal MemberToken[] GetTokenByAccount(string account)
        {
            string temp = account != null ? account.Trim() : string.Empty;
            Check.NotEmpty(temp);

            lock (_locker)
            {
                return this.Values.Where(w => w.UserName == temp || w.LoginCode == temp).ToArray();
            }
        }

        /// <summary>
        /// 根据指定的登录身份信息的Token标识获取该Token标识的登录身份信息。
        /// <para>如果该方法返回 null，则说明该 <paramref name="key"/> 标识无效。</para>
        /// </summary>
        /// <param name="key">字符串格式的登录身份信息的Token标识</param>
        /// <returns></returns>
        internal MemberToken GetTokenByGuid(string key)
        {
            string temp = key != null ? key.Trim() : string.Empty;
            Check.NotEmpty(temp);

            Guid gkey = Guid.Empty;
            if (Guid.TryParse(temp, out gkey))
            { return this.GetTokenByGuid(gkey); }
            else
            { return null; }
        }

        /// <summary>
        /// 根据指定的登录身份信息的Token标识获取该Token标识的登录身份信息。
        /// <para>如果该方法返回 null，则说明该 <paramref name="key"/> 无效。</para>
        /// </summary>
        /// <param name="key">登录身份信息的Token标识</param>
        /// <returns></returns>
        internal MemberToken GetTokenByGuid(Guid key)
        {
            return this.GetValue(key);
        }
        
        /// <summary>
        /// 根据指定的登录身份信息的 IMEI 值获取该登录身份信息。
        /// <para>如果该方法返回 null，则说明该 <paramref name="imei"/> 无效。</para>
        /// </summary>
        /// <param name="imei"></param>
        /// <returns></returns>
        internal MemberToken GetTokenByIMEI(string imei)
        {
            string temp = imei != null ? imei.Trim() : string.Empty;
            Check.NotEmpty(temp);
            return this.Values.FirstOrDefault(item => item.IsPhoneLogin && temp.Equals(item.IMEI, StringComparison.InvariantCultureIgnoreCase));
        }

        internal IEnumerable<MemberToken> GetOnlineTokens()
        {
            return this.Values.Where(item => item.ValidateLoginState());
        }

        #endregion

        #region 内部方法

        /// <summary>
        /// 初始化 Session 状态计时器
        /// </summary>
        private void InitTimer()
        {
            int interval = Member.Common.Config.SessionInterval;
            if (interval == 0)
            { return; }

            int dueTime = interval * 1000 * 60;
            int period = interval * 1000 * 60;
            this._timer = new Timer(ClearTimeoutUsers, this, dueTime, period);
        }

        /// <summary>
        /// 获取已过期的Tokens
        /// </summary>
        /// <param name="tokenDict"></param>
        /// <returns></returns>
        private static KeyValuePair<Guid, MemberToken>[] GetTimeoutTokens(MemberTokenCollection tokenDict)
        {
            Check.NotNull(tokenDict);
            return tokenDict.Where(item => item.Value.IsTimeout).ToArray();
        }

        /// <summary>
        /// 刷新集合中的登录身份信息，即移除已过期的登录身份信息
        /// </summary>
        /// <param name="state"></param>
        private static void ClearTimeoutUsers(object state)
        {
            MemberTokenCollection _this = state as MemberTokenCollection;
            if (_this == null)
            { return; }

            KeyValuePair<Guid, MemberToken>[] pairs = GetTimeoutTokens(_this);
            for (int i = 0; i < pairs.Length; i++)
            {
                KeyValuePair<Guid, MemberToken> item = pairs[i];
                if (item.Value != null)
                { item.Value.TimeoutLogout(); }
            }
        }

        #endregion
    }
}
